/*
*	Ivan Concua 12352
*	Roberto Andrino 12413
*	Algoritmos y estructuras de datos
*	Calculadora.java
*	
*	Instrucciones de uso: Se debe colocar un archivo con el nombre "datos.txt" en la
*	carpeta donde esten las tres clases del programa. El programa debe tener las
*	operaciones a realizar en el formato Postfix. Se aceptan solo dos operandos por operacion
*	Ejemplo: 1 2 - 4 / 3 + se toma como (4/(2-1)) + 3
*	El usuario solo podra crear una de las 5 opciones del menu a la vez. Si intenta
*	crear mas de un Stack o List a la vez, se lanzara una excepcion SingletonException
*/

import java.util.Scanner; // para leer el teclado y el archivo
import java.io.File; // para saber si el archivo existe
import java.io.FileNotFoundException; // excepcion para cuando no es encuentre el archivo

public class Calculadora
{
	public static void main(String[] args)
	{
		// parte de Factory design, que escoge que objeto crear segun lo que se quiera
		StackFactory<Double> stack_factory = new StackFactory<Double>();
		ListFactory<Double> list_factory = new ListFactory<Double>();
		
		// pila se debe inicializar. Si el usuario escoge otra opcion, pila sera un objeto de
		// una clase distinta de StackVector<Double>
		Stack<Double> pila = new StackVector<Double>();
		
		Scanner usuario = new Scanner(System.in); // para leer el teclado
		boolean escoger = true, continuar = true; // para los ciclos del menu

		while(continuar)
		{
			while(escoger)
			{
				System.out.println("\n1.Vector\n2.ArrayList\n3.Lista Simplemente encadenada");
				System.out.println("4.Lista Doblemente encadenada\n5.Lista circular\n6.Salir");
				System.out.print("Ingrese el tipo de stack que quiere usar (1, 2, 3): ");
				String tipo = usuario.nextLine();
				
				try // se puede lanzar una SingletonException
				{
					if(tipo.equals("1") || tipo.equals("2") || tipo.equals("3") || tipo.equals("4") || tipo.equals("5"))
					{
						// se llama a una de las Factory segun lo que escoja el usuario
						
						if(tipo.equals("1")|| tipo.equals("2"))
							pila = stack_factory.getStack(tipo);
						
						else
							pila = list_factory.getStack(tipo);
						
						// para asegurarse de que no se pueda crear mas de un objeto, de acuerdo al Singleton design
						list_factory.setInstanceFlag();
						stack_factory.setInstanceFlag();
						escoger = false; // para salir del menu
					}
					
					else if(tipo.equals("6"))
					{
						continuar = escoger = false; // para terminar el ciclo
						throw new Exception();
					}
						
					else
						System.out.println("Error: opcion invalida");
						
					Scanner scanner = new Scanner(new File("datos.txt")); // para leer el archivo
					System.out.println("ENTRADA \tOPERACION \t\tPILA");
					
					while(scanner.hasNext())
					{
						String linea = scanner.next(); // se leen todos los caracteres del archivo
						boolean negativo = false; // indicador si un numero es negativo
						
						if(linea.equals("-"))
							negativo = true;
							
						if(Character.isDigit(linea.charAt(0)) || linea.length() == 2)
						{
							double resultado = Double.parseDouble(linea); // se convierte la cadena a Double
							
							if(negativo) // si hay un signo negativo, se multiplica por -1 el operando
								resultado *= -1;
							
							if((pila.size() + 1) > 1)
								System.out.println(linea + "\t\tpush operando\t\t" + pila.peek() + ", " + resultado);
								
							else
								System.out.println(linea + "\t\tpush operando\t\t" + resultado);
							
							pila.push(resultado); // la cadena convertida a Double entra al stack
						}
						
						else
						{
							// para cada operacion se usan los primeros dos valores del stack
							// el resultado se guarda en la pila
							if(linea.equals("+"))
							{
								pila.push(pila.pop() + pila.pop());
								System.out.println(linea + "\t\tsumar\t\t\t" + pila.peek());
							}
							
							else if(linea.equals("-"))
							{
								pila.push(pila.pop() - pila.pop());
								System.out.println(linea + "\t\trestar\t\t\t" + pila.peek());
							}
								
							else if(linea.equals("*"))
							{
								pila.push(pila.pop() * pila.pop());
								System.out.println(linea + "\t\tmultiplicar\t\t" + pila.peek());
							}
							
							else if(linea.equals("/"))
							{
								try // podria lanzar excepcion por division por cero
								{
									pila.push(pila.pop() / pila.pop());
									
									if(pila.peek().isInfinite()) // division por cero
										throw new Exception(); // se lanza una excepcion
									
									System.out.println(linea + "\t\tdividir \t\t" + pila.peek());
								} catch(Exception e)
								{
									throw new ArithmeticException(); // se lanza otra excepcion
								}
							}
						} 
					}
				
					System.out.println("\nResultado: " + pila.peek() + "\nClase usada: " + pila.getClass());
				}
				catch(FileNotFoundException fnfe) // si no se encuentra el archivo
				{
					System.out.println("Error: no se encontro el archivo \"datos.txt\".");
				}
				catch(ArithmeticException ae) // si hay division por cero
				{
					System.out.println("Error: division por cero.");
				}
				catch(SingletonException se) // si se intenta crear mas de un objeto de Stack o List
				{
					System.out.println(se.getMessage());
				}
				catch(Exception e){} // cualquier otra excepcion (como la que se lanza al elegir Salir del menu
			}
			
			escoger = true; // para continuar el ciclo hasta que el usuario salga del programa
		}
	}	
}
